#!/usr/bin/env python2.5

## Defines, and redefines if necessary, a word for a given topical context.

## Usage:  ~    (meant to be used modularly)

import nltk, re, string, ask
from nltk import corpus
from nltk.corpus import wordnet

def yes(string):
	"""Returns True if the string is an affirmation."""
	if (string.lower() in ["y", "yes", "yeah", "yup", "ok"]):	return True
	else:	return False

def define(word, Webster, bestdef, changed, old_topic, new_topic):
	"""Defines a word, if desired by the user, and if the topic has changed."""
	import answer
	if ((Webster != "") and (not changed)):	return (False, Webster)
	if (Webster == ""):
		answer.write("The word " + word + " was not defined under the topic " + old_topic + ".")
	else:
		asked = ask.getPlay("The word " + word + " was defined under the topic " + old_topic + " as " + Webster + ".\nDo you want this meaning to carry over to the new topic " + new_topic + "?  ")
		if yes(asked):
			return (False, Webster)
	undone = True
	dno = 1
	while (undone):
		if (dno == bestdef):	dno += 1
		string = word + ".n." + str(dno)
		try:
			if (dno < len(wordnet.synsets(word, pos = wordnet.NOUN))):
				asked = ask.getPlay("Does " + wordnet.synset(string).definition + " work for your usage of " + word + "?  ")
				undone = not yes(ask)
				newdef = wordnet.synset(string).definition
				dno += 1
			else:
				newdef = ask.getPlay("Then how would you define " + word + "?  ")
				undone = False
		except(Exception):
			newdef = ask.getPlay("How would you define " + word + "?  ")
			undone = False
	return (True, newdef)

def paran(str, lex, alt, best):
	"""Allows the user to self-define a word by putting its definition in parentheses."""
	allfinds = re.finditer(r"(\w+) ?\(([^)]*)\)", str)
	for found in allfinds:
		word = found.group(1)
		meaning = found.group(2)
		# print '"' + word + '" is defined here as "' + meaning + '".'
		if ((meaning == "...") or (meaning == "")):
			if (word in best):
				lex[word] = wordnet.synset(word + ".n." + repr(best[word])).definition
			else:
				lex[word] = "HERETOFORE UNDEFINED"
		else:
			lex[word] = meaning
		alt[word] = True
		str = re.sub(re.compile(" ?\(" + meaning + "\)"), "", str)
	return (str, lex, alt)

def paranNP(NPs, str, lex, alt, best):
	"""Allows the user to self-define a mutable noun phrase by putting its definition in parentheses."""
	for nouns in NPs.values():
		found = re.search(re.compile("(" + nouns + ") ?\(([^)]*)\)"), str)
		if (not found):	continue
		# print "Found", nouns
		word = found.group(1)
		meaning = found.group(2)
		# print '"' + word + '" is defined here as "' + meaning + '".'
		if ((meaning == "...") or (meaning == "")):
			if ((word in best) and (word + ".n." + repr(best[word]) in wordnet.synset)):
				lex[word] = wordnet.synset(word + ".n." + repr(best[word])).definition
			else:
				lex[word] = "HERETOFORE UNDEFINED"
		else:
			lex[word] = meaning
		alt[word] = True
	return (lex, alt)

## TODO:  Enable definitions to be nested.
def paranest(str, lex, alt):
	"""Allows the user to self-define a word by putting its definition in parentheses.
		This is the recursive counterpart to parans(...) above."""
	allfinds = re.finditer(r"(\w+) \(([^)]*)\)", str)
	for found in allfinds:
		word = found.group(1)
		meaning = found.group(2)
		# print '"' + word + '" is defined here as "' + meaning + '".'
		if (re.search(r"\(", meaning)):	(meaning, lex, alt) = paranest(meaning, lex, alt)
		lex[word] = meaning
		alt[word] = True
		str = re.sub(re.compile(" \(" + meaning + "\)"), "", str)
		# print str
	return (str, lex, alt)

def RB(tokens, tags, lex, alt, best):
	"""Just like paran(), this lets the user self-define a word; only, this happens after tokenization and tagging."""
	import getnps, tokenize
	x = 0
	while (x < len(tags)):
		meaning = ""
		if (tags[x] in ["-LRB-", "("]):
			x += 1
			while (tags[x] not in ["-RRB-", ")"]):
				meaning += tags[x] + " "
				if (x > len(tags)):	break
				x += 1
		else:	x += 1
		if (meaning == "... "):
			word = tokens[x - 1]
			if (word in best):
				lex[word] = wordnet.synset(word + ".n." + repr(best[word])).definition
			else:
				lex[word] = "HERETOFORE UNDEFINED"
			alt[word] = True
		else:
			if (meaning):
				word = tokens[x - 1]
				lex[word] = meaning[:-1]
				alt[word] = True
	return (lex, alt)


## Test code:
def test1(words, altered, ideal, old_topic, new_topic, changed):
	"""First test."""
	sent = [["The", "DT"], ["ice", "NN"], ["dilluted", "VBD"], ["the", "DT"], ["cocktail", "NN"], [".", "DOT"]]
	## Initial run:
	for (word, tag) in sent:
		if (tag in ["NN", "NNS", "FW"]):
			# (altered[word], words[word]) = define(word, "")
			altered[word] = True;
			
			## The ideal definition of a word, optional for now, given a particular topic.
			ideal[word] = 1
			string = word + ".n." + str(ideal[word])
			words[word] = wordnet.synset(string).definition
	for word in sent:
		print word[0], 
		if (word[0] in altered):	print "(" + words[word[0]] + ")",
	print "\n"
	## Unchanged run:
	changed = (new_topic != old_topic)
	if (not changed):	altered = dict()
	for (word, tag) in sent:
		if (tag in ["NN", "NNS", "FW"]):
			(altered[word], words[word]) = define(word, words[word], ideal[word], changed, old_topic, new_topic)
	for word in sent:
		print word[0], 
		if (word[0] in altered):	print "(" + words[word[0]] + ")",
	print "\n"
	## New topic:
	new_topic = "potpourri"
	changed = (new_topic != old_topic)
	if (not changed):	altered = dict()
	for (word, tag) in sent:
		if (tag in ["NN", "NNS", "FW"]):
			(altered[word], words[word]) = define(word, words[word], ideal[word], changed, old_topic, new_topic)
	for word in sent:
		print word[0], 
		if (word[0] in altered):	print "(" + words[word[0]] + ")",
	print "\n"

def test2(words, altered, ideal):
	"""Second test."""
	test = "I thought that anime (cartoons from Japan) would make a great gift for my GF (girlfriend) ."
	(test, words, altered) = paran(test, words, altered, ideal)
	for token in test.split():
		print token,
		if (token in altered):	print "(" + words[token] + ")",
	print
	test = 'I thought that "Rosario+Vampire" (an anime (cartoons from Japan)) would make a great gift for my GF (girlfriend) .'
	# (test, words, altered) = paranest(test, words, altered)
	(test, words, altered) = paran(test, words, altered, ideal)
	for token in test.split():
		print token,
		if (token in altered):	print "(" + words[token] + ")",

def test3(words, altered, ideal):
	"""Third test."""
	ideal["day"] = 1
	test = "She turned twenty (20) the day (...) before the equinox () on September twenty ."
	(test, words, altered) = paran(test, words, altered, ideal)
	for token in test.split():
		print token,
		if (token in altered):	print "(" + words[token] + ")",
	print

def test4(lex, alt, best):
	"""4th test."""
	lexes = ["Meet", "the", "jade", "macaque", "(", "monkey", ")", "beneath", "the", "willow", "trees", "of", "the", "Gobe", "Desert", "at", "noon", "-LRB-", "...", "-RRB-", "."]
	poss = ["VB", "DT", "NN", "NN", "-LRB", "NN", "-RRB", "IN", "DT", "NNS", "NNS", "IN", "DT", "FW", "NNP", "IN", "NN", "-LRB-", "SYM", "-RRB-", "DOT"]
	(lex, alt) = RB(lexes, poss, lex, alt, best)
	# for 

def test5(words, altered, ideal):
	"""750mL test."""
	import tokenize, getnps, seqtagger, re
	test = "Beneath the oceans (bodies of water) of Terra (Earth) are mold (...) up to 5 km (kilometers) in thickness()."
	noRBs = re.sub(r" ?\(([^)]*)\)", "", test)
	broken = tokenize.tokenize(noRBs, 0)
	# print broken
	(SeqTagger, td) = seqtagger.getTagger('sentences.dat', 0, 4)
	tagged = SeqTagger.tag(broken.split())
	tok = list()
	tag = list()
	for (word, pos) in tagged:
		tok.append(word)
		tag.append(pos)
	# print tok
	# print tag
	NPdict = getnps.NPs(tok, tag)
	(words, altered) = paranNP(NPdict, test, words, altered, ideal)
	for NP in NPdict.values():
		# print "Noun phrase:  " + NP
		noRBs = re.sub(NP, NP + " (" + words[NP] + ")", noRBs)
	print noRBs, "\n"

if (__name__ == "__main__"):
	## Topics and change flag
	old_topic = "hodgepodge"
	new_topic = old_topic
	changed = True
	words = dict()
	altered = dict()
	ideal = dict()
	test1(words, altered, ideal, old_topic, new_topic, changed)
	# test2(words, altered, ideal)
	# test3(words, altered, ideal)
	# test4(words, altered, ideal)
	# test5(words, altered, ideal)
